<template>
	<view class="list-view__container">
		<scroll-view class="list-view__inner-container" :refresher-enabled="_enableRefresh" :refresher-triggered="refreshing" @refresherrefresh="onRefreshData" @refresherrestore="onRefreshRestore" @refresherabort="onRefreshAbort" scroll-y="true">
			<view class="list-view__content">
				<view id="list-view__guideView" class="list-view__guideView" :style="{left: guideViewRect.left + 'px', top: guideViewRect.top + 'px', right: guideViewRect.right + 'px', bottom: guideViewRect.bottom + 'px'}"></view>
				<template v-if="hasNoDataHolder && ((Object.keys(datas || []) || []).length <= 0)">
					<no-more-holder class="no-more-holder" :image="noDataImage"></no-more-holder>
				</template>
				<slot name="content" :datas="datas" v-else></slot>
			</view>
			
			<view id="load-more">
				<template v-if="currentPage > 1 || hasMore">
					<uni-load-more v-if="datas && datas.length > 0 && enableLoadMore" :status="loadMoreStatus"></uni-load-more>
				</template>
			</view>
		</scroll-view>
		<view class="list-view__bottom-guide-view" id="list-view__bottom-guide-view"></view>
	</view>
</template>

<script>
	import { isPromise } from "../../utils/utils.js"
	export default {
		name:"list-view",
		props: {
			enableLoadMore: {
				type: Boolean,
				default: false
			},
			enableRefresh: {
				type: Boolean,
				default: false
			},
			guideObserverId: {
				type: String,
				default: ''
			},
			hasNoDataHolder: {
				type: Boolean,
				default: false
			},
			noDataImage: {
				type: String,
			},
		},
		data() {
			return {
				loadTarget: {},
				_enableRefresh: true,
				refreshing: false,
				currentPage: 1,
				hasMore: false,
				datas: [],
				loadMoreStatus: "more",
				guideViewRect: {
					left: 0,
					top: 0,
					right: 0,
					bottom: 0
				},
				loadMoreIntersection: undefined,
				topIntersection: undefined
			};
		},
		
		beforeMount: function() {
			/**
			 * 业务层使用该组件时，在事件回调方法中
			 * 通过event.tartget获取到本组件的实例
			 * 为本组件的loadPageData属性设置一个function， 接收{start, pageSize, showLoading = false}参数，并最终返回 
			 * Promise.resolve({
			 *     curPage: res.curPage,
			 *	   pageCount: res.pageCount,
			 *	   datas: res.datas
			 * })
			 */
			this.$emit("listinit", {target: this.loadTarget})
		},
		
		mounted: function() {
			this.triggerLoadData({start: 1, showLoading: true})
				.then((res) => {
					this.$nextTick(() => {
						this.checkIntersection()
					})
				})
			
			this.loadMoreIntersection = uni.createIntersectionObserver(this)
			this.loadMoreIntersection.relativeTo("#list-view__bottom-guide-view")
				.observe('#load-more', (res) => {
					if(res.intersectionRatio > 0) {
						//相交
						if(this.enableLoadMore && this.loadMoreStatus !== 'loading') {
							this.loadMoreData()
						}
					}
				})
		},
		
		unmounted() {
			if(this.loadMoreIntersection) {
				this.loadMoreIntersection.disconnect()
			}
			
			if(this.topIntersection) {
				this.topIntersection.disconnect()
			}
		},
		
		methods: {
			triggerLoadData({start = 1, pageSize = 20, showLoading = false} = {}) {
				this.currentPage = start
				if(this.loadTarget.loadPageData) {
					let result = this.loadTarget.loadPageData({start: start, pageSize: pageSize, showLoading: showLoading})
					if(isPromise(result)) {
						return result.then(res => {
							this.hasMore = res.curPage < (res.pageCount || 1)
							if(res.curPage <= 1) {
								this.datas = res.datas
							} else {
								let tempArray = res.datas
								this.datas = this.datas.concat(tempArray)
							}
							if(this.hasMore) {
								this.loadMoreStatus = "more"
							} else {
								this.loadMoreStatus = "no-more"
							}
							
							let parent = this.getParent()
							if(parent) {
								parent.setListData(this.datas)
							}
							return Promise.resolve()
						})
						.finally(() => {
							// this._enableRefresh = false
							this.$nextTick(() => {
								this._enableRefresh = this.enableRefresh
							})
						})
					} else {
						this.hasMore = false
						this.loadMoreStatus = "no-more"
						// this._enableRefresh = false
						this.$nextTick(() => {
							this._enableRefresh = this.enableRefresh
						})
						return Promise.resolve()
					}
				} else {
					this.hasMore = false
					this.loadMoreStatus = "no-more"
					// this._enableRefresh = false
					this.$nextTick(() => {
						this._enableRefresh = this.enableRefresh
					})
					return Promise.resolve()
				}
			},
			
			onRefreshData: function() {
				this.refreshing = true
				this.triggerLoadData({start: 1})
					.finally(() => {
						this.refreshing = false
					})
			},
			
			onRefreshRestore: function() {
				this.refreshing = false
			},
			
			onRefreshAbort: function() {
				this.refreshing = false
			},
			
			loadMoreData: function() {
				if(this.loadMoreStatus === "no-more") {
					return
				}
				let page = this.currentPage
				if(this.loadMoreStatus != "error") {
					page += 1
				}
				this.loadMoreStatus = "loading"
				this.triggerLoadData({start: page})
					.then((res) => {
						if(this.hasMore) {
							this.loadMoreStatus = "more"
						} else {
							this.loadMoreStatus = "no-more"
						}
					})
					.catch((err) => {
						this.loadMoreStatus = "error"
						return Promise.reject(err)
					})
			},
			
			/**
			 * 获取父元素实例
			 */
			getParent(name = 'custom-page') {
				let parent = this.$parent;
				let parentName = parent.$options.name;
				while (parentName !== name) {
					parent = parent.$parent;
					if (!parent) return false
					parentName = parent.$options.name;
				}
				return parent;
			},
			//检测布局相交状态
			checkIntersection: function() {
				if(!this.guideObserverId) {
					return
				}
				let top = 0
				let height = 0
				let statusBarHeight = 0
				let screenHeight = 0
				// #ifdef MP
				let menuButtonRect = uni.getMenuButtonBoundingClientRect()
				let systemInfo = uni.getSystemInfoSync()
				
				top = menuButtonRect.top
				height = menuButtonRect.height
				statusBarHeight = systemInfo.statusBarHeight
				screenHeight = systemInfo.screenHeight
				// #endif
				
				// #ifdef APP-PLUS
				let systemInfo = uni.getSystemInfoSync()
				statusBarHeight = systemInfo.statusBarHeight
				top = statusBarHeight + 4
				height = 32
				screenHeight = systemInfo.screenHeight
				// #endif
				
				// #ifdef WEB
				let systemInfo = uni.getSystemInfoSync()
				statusBarHeight = 0
				top = statusBarHeight + 4
				height = 32
				screenHeight = systemInfo.screenHeight
				// #endif
			
				let navgationBarHeight = statusBarHeight + Math.max(44, 2*(top - statusBarHeight) + height)
				uni.createSelectorQuery()
					.select("#" + this.guideObserverId)
					.boundingClientRect()
					.in(this)
					.selectViewport()
					.boundingClientRect()
					.exec(res => {
						this.guideViewRect = {
							left: 0,
							top: 128,
							right: 0,
							bottom: 0
						}
						
						let relativeViewport = {
							left: 0,
							right: 0,
							top: 0,
							bottom: 0
						}
						relativeViewport.top = res[1].top - navgationBarHeight
						relativeViewport.bottom = (navgationBarHeight + 1) - screenHeight
						
						this.topIntersection = uni.createIntersectionObserver(this)
						this.topIntersection.relativeToViewport(relativeViewport)
							.observe('#' + 'list-view__guideView', (res) => {
								let intersectionRect = (res || {}).intersectionRect || {}
								let intersectionTop = intersectionRect.top || 0
								let intersectionBottom = intersectionRect.bottom || 0
								if(res.intersectionRatio > 0 || intersectionTop > 0 || intersectionBottom > 0) {
									//相交
									this.$emit("giudeviewintersectionchange", {intersection: true})
								} else {
									//不相交
									this.$emit("giudeviewintersectionchange", {intersection: false})
								}
							})
					})
			},
			getListData: function() {
				return this.datas
			},
		}
	}
</script>

<style scoped>
	.list-view__container {
		position: relative;
		width: 100%;
		height: 100%;
		overflow: hidden;
	}
	
	.list-view__bottom-guide-view {
		position: absolute;
		bottom: 0;
		left: 0;
		width: 100%;
		height: 1px;
		background: transparent;
		z-index: -1;
	}
	
	.list-view__inner-container {
		width: 100%;
		height: 100%;
		overflow: hidden;
	}
	
	.list-view__content {
	  width: 100%;
	  min-height: 100%;
	  display: flex;
	  flex-direction: column;
	  align-items: stretch;
	  position: relative;
	}
	
	.list-view__guideView {
		position: absolute;
		background: transparent;
		z-index: -9999;
	}
	.no-more-holder {
		display: block;
		position: absolute;
		left: 0;
		top: 0;
		width: 100%;
		height: 100%;
		overflow: hidden;
		background-color: white;
		/* #ifdef H5 */
		text-align: center;
		/* #endif */
	}
</style>